package cn.xxm.dongbao.user.secure.dos;

import cn.xxm.dongbao.user.secure.access.DosAccessDeniedHandler;
import cn.xxm.dongbao.user.secure.access.DosAccessDeniedHandlerImpl;
import org.apache.commons.lang.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.FilterConfig;

public class DosPrecautionFilter implements Filter {
    /**
     * Access denied handler
     */
    protected static final String ACCESS_DENIED_HANDLER = "access_denied_handler";
    /**
     * Filter configuration.
     */
    protected static final String HIT_FILTER_CONFIG = "hits";
    /**
     * Filter configuration
     */
    protected static final String PERIOD_FILTER_CONFIG = "period";
    /**
     * Filter configuration.
     */
    protected static final String REPOSITORY_FILTER_CONFIG = "repository";
    private DosAccessDeniedHandler accessDeniedHandler = new DosAccessDeniedHandlerImpl();
    private DosStatisticsProcess process = null;

    @Override
    public void destroy() {
        process = null;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        try {
            process.doStatistics(httpRequest);
            filterChain.doFilter(request, response);
        } catch (DosStatisticsException e) {
            // handle dos attack
            accessDeniedHandler.handle(httpRequest, httpResponse, e);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        DosConfig config = new DosConfig();
        config.setHits(filterConfig.getInitParameter(HIT_FILTER_CONFIG));
        config.setPeriod(filterConfig.getInitParameter(PERIOD_FILTER_CONFIG));
        DosStatisticsRepository dosStatisticsRepository;
        String repository = filterConfig.getInitParameter(REPOSITORY_FILTER_CONFIG);
        if (StringUtils.isNotBlank(repository)) {
            dosStatisticsRepository = creatRepositoryInstance(repository);
        } else {
            dosStatisticsRepository = new HttpSessionDosStatisticsRepository();
        }
        process = new DosStatisticsProcess(dosStatisticsRepository, config);
        String handler = filterConfig.getInitParameter(ACCESS_DENIED_HANDLER);
        if (StringUtils.isNotBlank(handler)) {
            accessDeniedHandler = createHandlerInstance(handler);
        }
    }

    /**
     * Gets the dos statistics repository.
     */
    private DosStatisticsRepository creatRepositoryInstance(String value) throws ServletException {
        try {
            Object repositoryInstance = Class.forName(value).newInstance();
            if (repositoryInstance instanceof DosStatisticsRepository) {
                return (DosStatisticsRepository) repositoryInstance;
            } else {
                throw new ServletException("Reflect DosStatisticsRepository failed.");
            }
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
            throw new ServletException("Reflect DosStatisticsRepository failed! " + ex.getMessage(), ex);
        }
    }

    /**
     * Gets the access denied handler.
     */
    private DosAccessDeniedHandler createHandlerInstance(String handler) throws ServletException {
        try {
            Object handlerInstance = Class.forName(handler).newInstance();
            if (handlerInstance instanceof DosAccessDeniedHandler) {
                return (DosAccessDeniedHandler) handlerInstance;
            } else {
                throw new ServletException("Reflect DosAccessDeniedHandler failed.");
            }
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
            throw new ServletException("Reflect DosAccessDeniedHandler failed! " + ex.getMessage(), ex);
        }
    }
}